package composite;

import interpreter.ShapeBase;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Observable;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import commandPattern.AddObject;
import commandPattern.ChangeColor;
import commandPattern.Command;
import commandPattern.Move;
import commandPattern.RemoveObject;
import commandPattern.Scale;

import GUI.EditPanel;
import GUI.FuncPanel;
import GUI.ModePanel;

/**
 * Composite pattern entry point.
 * rootGroup represents the whole drawing document.
 * It is observed by the observers.
 */
public class DocumentManager extends Observable {
	/** Place for the shapeBase singleton object */
	ShapeBase shapeBase;
	/** The main node of the document */
	ObjectGroup rootGroup;
	/** How far we are in the past (0 - present) */
	int undoCount = 0;
	    
    /** Stores the selected shape */
    ObjectItem selectedShape;
        
    /** Commands - command pattern */
    ChangeColor changeColor = new ChangeColor();
    Move		move		= new Move();
    Scale		scale		= new Scale();
    AddObject   addObject	= new AddObject();
    RemoveObject removeOb	= new RemoveObject();
    
	/** Command stack to support Undo and Redo */
    ArrayList<Command> commandStack = new ArrayList<Command>();
	
    /** Variables needed to determine whether shape was scaled/moved recently */
    boolean scaled = false;
    boolean moved = false;
    
	public DocumentManager(){
		rootGroup = new ObjectGroup();
		//Prepare the addObject and removeOb commands
		addObject.setRootGroup(rootGroup);
		removeOb.setRootGroup(rootGroup);
	}
			
	/**
	 * Notifies the observers about change in the rootGroup
	 */
	public void refreshRoot(){
		setChanged();
	    notifyObservers(rootGroup);
	}
	
	public void addCommandToStack(Command command){
		commandStack.add(command);
		undoCount = 0;
	}
	 
	/**
	 * Listen to the mouse actions in the drawing panel
	 * small TODO: now it actually gets the position of mouse clicked in a whole applet
	 */
	public class DrawingPanelMouseListener extends MouseAdapter {
	    public void mousePressed(MouseEvent e){    	  
	    	if(ModePanel.currentMode == "Edit" && selectedShape != null){
		  	  	if(EditPanel.currentEditMode == "Move"){
		  	  		move.setObjectItem(selectedShape);
		  	  		move.setOldPosX(selectedShape.getPosX());
		  	  		move.setOldPosY(selectedShape.getPosY());
		  	  	}
		  	  	if(EditPanel.currentEditMode == "Scale"){
		  	  		scale.setObjectItem(selectedShape);
		  	  		scale.setOldScaleX(selectedShape.getScaleX());
		  	  		scale.setOldScaleY(selectedShape.getScaleY());
		  	  	}
	    	}
	    }
	    public void mouseReleased(MouseEvent e) {
	    	//System.out.println("Mouse: released");
	    	if(scaled){
	    		Command command = scale.clone();
	    		command.execute();
	    		addCommandToStack(command);
	    		scaled = false;
	    	}
	    	if(moved){
	    		move.setObjectItem(selectedShape);
	    		Command command = move.clone();
	    		command.execute();
	    		addCommandToStack(command);
	    		moved = false;
	    	}
	    }
	
	    public void mouseClicked(MouseEvent e) {
	    	if(ModePanel.currentMode == "Draw"){
	    		addObject.setPosX(e.getX());
	    		addObject.setPosY(e.getY());
	    		Command command = addObject.clone();
	    		command.execute();
	    		addCommandToStack(command);
	    		refreshRoot();
	    	}
	    	else if (ModePanel.currentMode == "Edit"){
	    		// Check if any shape is hit, and return it as selected
	    		ObjectItem selected = null;
	    		for (int i = 0; i < rootGroup.getObjectItems().size(); i++) {
	    			Rectangle2D boundsRec = rootGroup.getObjectItems().get(i).getBoundsRectangle();
	    			if(boundsRec.contains(e.getX(), e.getY()))
	    				selected = rootGroup.getObjectItems().get(i);
				}
	    		if(selected != null){
		    		if (!selected.isSelected()){
		    			//Select new shape
		    			if(selectedShape != null)
		    				selectedShape.setSelected(false); // Unselect old shape

		    			selected.setSelected(true);
		    			selectedShape = selected;
		    		}else{
		    			// Unselect shape
		    			selectedShape.setSelected(false);
		    			selectedShape = null;
		    		}
		    		// Remove shape
	        		if(EditPanel.currentEditMode == "Remove"){
	        			removeOb.setOldObject(selectedShape);
	        			Command command = removeOb.clone();
	        			command.execute();
	        			addCommandToStack(command);
	        			selectedShape = null;
	        			refreshRoot();
	        		}
		    	}
				refreshRoot();
	    	}//select mode
	    }   
	}
	
	/**
	 * Listen to the function buttons
	 */
	public class FuncButtonsListener implements ActionListener{		
		public void actionPerformed(ActionEvent e) {
			// Undo button - command pattern
			if(e.getActionCommand() == "Undo"){
				if(undoCount < commandStack.size()){
					commandStack.get(commandStack.size() - undoCount -1).undo();
					undoCount++;
					refreshRoot();
				}
			// Redo button
			}else if (e.getActionCommand() == "Redo"){
				if (undoCount > 0){
					commandStack.get(commandStack.size() - undoCount).redo();
					undoCount--;	
					refreshRoot();
				}
			} 
		}
	}
	
	/**
	 * Action called when chosen color is changed
	 */
	public class ColorChange implements ChangeListener{
		public void stateChanged(ChangeEvent e) {
			// Set the static variables on the FunctionPanel
			FuncPanel.current_color = FuncPanel.chooser.getColor();
			FuncPanel.button_color.setBackground(FuncPanel.current_color);
			
			if(selectedShape != null){
			// Set the color for the selected shape
				changeColor.setObjectItem(selectedShape);
				Command command = changeColor.clone();
				command.execute();
				addCommandToStack(command);
			}
            refreshRoot();
		}
	}
	
	/** 
	 * Smooth shape scale and move 
	 */
    public class MyMouseMotionListener extends MouseMotionAdapter {
    	int x,y,x1,x2,y1,y2;
    	
        public void mouseDragged(MouseEvent e) {
        	if(selectedShape != null){
        		if(EditPanel.currentEditMode == "Move"){
		            x2 = e.getX();
		            y2 = e.getY();
		        	x = x + x2 - x1;
		        	y = y + y2 - y1;
		        	x1 = x2;
		        	y1 = y2;
		        	selectedShape.moveObject(x, y);
		        	moved = true;
		            refreshRoot();
        		}
        		else if(EditPanel.currentEditMode == "Scale"){
		            x2 = e.getX();
		            y2 = e.getY();
		        	x =  x + x2 - x1;
		        	y =  y + y2 - y1;
		        	x1 = x2;
		        	y1 = y2;
		        	
		        	double scaleX = (double)x/(double)100;
		        	double scaleY = (double)y/(double)100;
		        	
		        	selectedShape.scale(scaleX, scaleY);
		        	scaled = true;
		            refreshRoot();
        		}
        	}
        }
    }
}// Document Manager
